package ar.edu.uba.fi.jmit.aspecter;

import java.awt.geom.Point2D;
import java.util.LinkedList;
import java.util.List;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;
import ar.edu.uba.fi.jmit.aspecter.core.ConstructorDefinition;
import ar.edu.uba.fi.jmit.aspecter.core.exception.AspecterRuntimeException;
import ar.edu.uba.fi.jmit.aspecter.testUtils.Point;

public class ConstructorDefinitionTestCase extends AspecterTestCase {

	public void testToString() {
		ConstructorDefinition def = new ConstructorDefinition(Point.class, new LinkedList<Class<?>>());
		assertEquals("ar.edu.uba.fi.jmit.aspecter.testUtils.Point()", def.toString());

		List<Class<?>> paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		def = new ConstructorDefinition(Point.class,paramsList);
		assertEquals("ar.edu.uba.fi.jmit.aspecter.testUtils.Point(int)", def.toString());
		
		paramsList = new LinkedList<Class<?>>();
		paramsList.add(String.class);
		def = new ConstructorDefinition(Point.class,paramsList);
		assertEquals("ar.edu.uba.fi.jmit.aspecter.testUtils.Point(java.lang.String)", def.toString());
	}
	
	public void testConstructor() {
		try {
			new ConstructorDefinition(null);
			fail("Exception should be thrown");
		}catch(AspecterRuntimeException e) {
			//expected
		}
		
		try {
			new ConstructorDefinition(null, null);
			fail("Exception should be thrown");
		}catch(AspecterRuntimeException e) {
			//expected
		}
		
		try {
			new ConstructorDefinition(Point.class, new LinkedList<Class<?>>());
		}catch(AspecterRuntimeException e) {
			fail(e);
		}
		
		try {
			new ConstructorDefinition(Point.class);
		}catch(AspecterRuntimeException e) {
			fail(e);
		}
	}
	
	public void testEquals() {
		ConstructorDefinition def1 = new ConstructorDefinition(Point.class,new LinkedList<Class<?>>());
		ConstructorDefinition def2 = new ConstructorDefinition(Point.class, new LinkedList<Class<?>>());
		assertEquals(def1, def2);
		def2 = new ConstructorDefinition(Point.class, null);
		assertFalse(def1.equals(def2));
		def2 = new ConstructorDefinition(MethodDefinitionTestCase.class, new LinkedList<Class<?>>());
		assertFalse(def1.equals(def2));
		
		List<Class<?>> paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		paramsList.add(long.class);
		paramsList.add(String.class);
		def1 = new ConstructorDefinition(Point.class, paramsList);
		def2 = new ConstructorDefinition(Point.class, paramsList);
		assertEquals(def1, def2);
		
		paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		paramsList.add(long.class);
		paramsList.add(String.class);
		def2 = new ConstructorDefinition(Point.class, paramsList);
		assertEquals(def1, def2);
		
		paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		paramsList.add(long.class);
		def2 = new ConstructorDefinition(Point.class, paramsList);
		assertFalse(def1.equals(def2));
		
		paramsList = new LinkedList<Class<?>>();
		paramsList.add(int.class);
		paramsList.add(String.class);
		paramsList.add(long.class);
		def2 = new ConstructorDefinition(Point.class, paramsList);
		assertFalse(def1.equals(def2));
		
		def2 = new ConstructorDefinition(Point.class, null);
		assertFalse(def1.equals(def2));
	}
	
	public void testMatch() {
		try {
			ConstructorDefinition def1 = new ConstructorDefinition(Point.class, null);
			CtConstructor ctConstructor1 = CtNewConstructor.make(new CtClass[]{}, new CtClass[]{}, ClassPool.getDefault().get(Point.class.getName()));
			assertTrue(def1.match(ctConstructor1));
			CtConstructor ctConstructor2 = CtNewConstructor.make(new CtClass[]{CtClass.intType, CtClass.longType}, new CtClass[]{}, ClassPool.getDefault().get(Point.class.getName()));
			assertTrue(def1.match(ctConstructor2));
			
			ConstructorDefinition def2 = new ConstructorDefinition(Point.class, new LinkedList<Class<?>>());
			assertTrue(def2.match(ctConstructor1));
			assertFalse(def2.match(ctConstructor2));
			
			ConstructorDefinition def4 = new ConstructorDefinition(Point2D.class, null);
			assertFalse(def4.match(ctConstructor1));
			assertFalse(def4.match(ctConstructor2));
		} catch (Exception e) {
			fail(e);
		} 
	}
	
	public void testHashCode() {
		List<ConstructorDefinition> constructorDefinitions = new LinkedList<ConstructorDefinition>();
		constructorDefinitions.add(new ConstructorDefinition(Point.class, null));
		constructorDefinitions.add(new ConstructorDefinition(Point.class, new LinkedList<Class<?>>()));
		List<Class<?>> params = new LinkedList<Class<?>>();
		params.add(long.class);
		params.add(Integer.class);
		params.add(Point.class);
		constructorDefinitions.add(new ConstructorDefinition(Point.class, params));
		params = new LinkedList<Class<?>>();
		params.add(Long.class);
		params.add(Integer.class);
		params.add(Point.class);
		constructorDefinitions.add(new ConstructorDefinition(Point.class, params));
		params = new LinkedList<Class<?>>();
		params.add(Long.class);
		constructorDefinitions.add(new ConstructorDefinition(Point.class, params));
		
		for(ConstructorDefinition definitionLevel1:constructorDefinitions) {
			for(ConstructorDefinition definitionLevel2:constructorDefinitions) {
				assertEquals(definitionLevel1.equals(definitionLevel2), definitionLevel1.hashCode() == definitionLevel2.hashCode());
			}
		}
	}
}
